"""
AI Agent Pro - 安全认证和权限控制系统
"""

import hashlib
import secrets
from datetime import datetime, timedelta
from typing import Any, Dict, Optional, List, Union

import bcrypt
import jwt
from fastapi import Depends, HTTPException, status, Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials, APIKeyHeader
from passlib.context import CryptContext
from pydantic import BaseModel, EmailStr

from app.core.config import get_settings
from app.core.logging import get_logger, log_security_event
from app.models import User, ApiKey
from app.core.database import get_db

logger = get_logger(__name__)
settings = get_settings()

# 密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# JWT设置
ALGORITHM = settings.algorithm
SECRET_KEY = settings.secret_key
ACCESS_TOKEN_EXPIRE_MINUTES = settings.access_token_expire_minutes

# 安全相关的依赖
security = HTTPBearer()
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)


# ====== 认证相关模型 ======

class Token(BaseModel):
    """访问令牌模型"""
    access_token: str
    token_type: str = "bearer"
    expires_in: int
    user_id: str
    permissions: List[str] = []


class TokenData(BaseModel):
    """令牌数据模型"""
    user_id: Optional[str] = None
    username: Optional[str] = None
    permissions: List[str] = []
    exp: Optional[datetime] = None


class LoginRequest(BaseModel):
    """登录请求模型"""
    username: str
    password: str
    remember_me: bool = False


class RegisterRequest(BaseModel):
    """注册请求模型"""
    username: str
    email: EmailStr
    password: str
    full_name: Optional[str] = None


class PasswordChangeRequest(BaseModel):
    """密码修改请求"""
    current_password: str
    new_password: str


class PasswordResetRequest(BaseModel):
    """密码重置请求"""
    email: EmailStr


class ApiKeyCreateRequest(BaseModel):
    """API密钥创建请求"""
    name: str
    permissions: List[str] = []
    expires_days: Optional[int] = None


class ApiKeyResponse(BaseModel):
    """API密钥响应"""
    id: str
    name: str
    key: Optional[str] = None  # 只在创建时返回
    permissions: List[str]
    is_active: bool
    last_used: Optional[datetime]
    expires_at: Optional[datetime]
    created_at: datetime


# ====== 权限定义 ======

class Permission:
    """权限常量"""
    # 用户权限
    USER_READ = "user:read"
    USER_WRITE = "user:write"
    USER_DELETE = "user:delete"
    
    # 智能体权限
    AGENT_CREATE = "agent:create"
    AGENT_READ = "agent:read"
    AGENT_WRITE = "agent:write"
    AGENT_DELETE = "agent:delete"
    AGENT_EXECUTE = "agent:execute"
    
    # 对话权限
    CONVERSATION_CREATE = "conversation:create"
    CONVERSATION_READ = "conversation:read"
    CONVERSATION_DELETE = "conversation:delete"
    
    # 工具权限
    TOOL_USE = "tool:use"
    TOOL_CREATE = "tool:create"
    TOOL_MANAGE = "tool:manage"
    
    # 系统权限
    SYSTEM_ADMIN = "system:admin"
    SYSTEM_MONITOR = "system:monitor"
    SYSTEM_CONFIG = "system:config"
    
    # 预定义角色
    ROLES = {
        "user": [
            USER_READ, AGENT_CREATE, AGENT_READ, AGENT_WRITE, AGENT_EXECUTE,
            CONVERSATION_CREATE, CONVERSATION_READ, CONVERSATION_DELETE, TOOL_USE
        ],
        "premium": [
            USER_READ, AGENT_CREATE, AGENT_READ, AGENT_WRITE, AGENT_DELETE, AGENT_EXECUTE,
            CONVERSATION_CREATE, CONVERSATION_READ, CONVERSATION_DELETE, 
            TOOL_USE, TOOL_CREATE
        ],
        "admin": [
            USER_READ, USER_WRITE, USER_DELETE,
            AGENT_CREATE, AGENT_READ, AGENT_WRITE, AGENT_DELETE, AGENT_EXECUTE,
            CONVERSATION_CREATE, CONVERSATION_READ, CONVERSATION_DELETE,
            TOOL_USE, TOOL_CREATE, TOOL_MANAGE,
            SYSTEM_ADMIN, SYSTEM_MONITOR, SYSTEM_CONFIG
        ]
    }


# ====== 密码和哈希工具 ======

class PasswordManager:
    """密码管理器"""
    
    @staticmethod
    def hash_password(password: str) -> str:
        """哈希密码"""
        return pwd_context.hash(password)
    
    @staticmethod
    def verify_password(plain_password: str, hashed_password: str) -> bool:
        """验证密码"""
        return pwd_context.verify(plain_password, hashed_password)
    
    @staticmethod
    def generate_password(length: int = 12) -> str:
        """生成随机密码"""
        alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
        return ''.join(secrets.choice(alphabet) for _ in range(length))
    
    @staticmethod
    def check_password_strength(password: str) -> Dict[str, Any]:
        """检查密码强度"""
        checks = {
            "length": len(password) >= 8,
            "has_upper": any(c.isupper() for c in password),
            "has_lower": any(c.islower() for c in password), 
            "has_digit": any(c.isdigit() for c in password),
            "has_special": any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in password)
        }
        
        strength_score = sum(checks.values())
        
        if strength_score >= 4:
            strength = "strong"
        elif strength_score >= 3:
            strength = "medium"
        else:
            strength = "weak"
        
        return {
            "strength": strength,
            "score": strength_score,
            "checks": checks,
            "suggestions": [
                "至少8个字符" if not checks["length"] else None,
                "包含大写字母" if not checks["has_upper"] else None,
                "包含小写字母" if not checks["has_lower"] else None,
                "包含数字" if not checks["has_digit"] else None,
                "包含特殊字符" if not checks["has_special"] else None,
            ]
        }


# ====== JWT令牌管理 ======

class TokenManager:
    """JWT令牌管理器"""
    
    @staticmethod
    def create_access_token(
        data: Dict[str, Any],
        expires_delta: Optional[timedelta] = None
    ) -> str:
        """创建访问令牌"""
        to_encode = data.copy()
        
        if expires_delta:
            expire = datetime.utcnow() + expires_delta
        else:
            expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        
        to_encode.update({"exp": expire})
        
        encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
        return encoded_jwt
    
    @staticmethod
    def verify_token(token: str) -> Optional[TokenData]:
        """验证令牌"""
        try:
            payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
            
            user_id: str = payload.get("sub")
            username: str = payload.get("username")
            permissions: List[str] = payload.get("permissions", [])
            exp: Optional[datetime] = payload.get("exp")
            
            if user_id is None:
                return None
            
            # 检查过期时间
            if exp and datetime.utcnow() > datetime.fromtimestamp(exp):
                return None
            
            return TokenData(
                user_id=user_id,
                username=username, 
                permissions=permissions,
                exp=datetime.fromtimestamp(exp) if exp else None
            )
            
        except jwt.JWTError:
            return None
    
    @staticmethod
    def create_refresh_token(user_id: str) -> str:
        """创建刷新令牌"""
        data = {"sub": user_id, "type": "refresh"}
        expire = datetime.utcnow() + timedelta(days=30)  # 30天有效期
        data.update({"exp": expire})
        
        return jwt.encode(data, SECRET_KEY, algorithm=ALGORITHM)


# ====== API密钥管理 ======

class ApiKeyManager:
    """API密钥管理器"""
    
    @staticmethod
    def generate_api_key() -> str:
        """生成API密钥"""
        # 生成32字节的随机密钥，转换为64字符的十六进制字符串
        key = secrets.token_hex(32)
        return f"aap_{key}"  # aap = AI Agent Pro
    
    @staticmethod
    def hash_api_key(api_key: str) -> str:
        """哈希API密钥"""
        return hashlib.sha256(api_key.encode()).hexdigest()
    
    @staticmethod
    async def create_api_key(
        db,
        user_id: str,
        name: str,
        permissions: List[str] = None,
        expires_days: Optional[int] = None
    ) -> tuple[str, ApiKey]:
        """创建API密钥"""
        api_key = ApiKeyManager.generate_api_key()
        key_hash = ApiKeyManager.hash_api_key(api_key)
        
        expires_at = None
        if expires_days:
            expires_at = datetime.utcnow() + timedelta(days=expires_days)
        
        db_api_key = ApiKey(
            user_id=user_id,
            name=name,
            key_hash=key_hash,
            permissions=permissions or [],
            expires_at=expires_at
        )
        
        db.add(db_api_key)
        await db.commit()
        await db.refresh(db_api_key)
        
        return api_key, db_api_key
    
    @staticmethod
    async def verify_api_key(db, api_key: str) -> Optional[ApiKey]:
        """验证API密钥"""
        if not api_key or not api_key.startswith("aap_"):
            return None
        
        key_hash = ApiKeyManager.hash_api_key(api_key)
        
        # 从数据库查询
        # result = await db.execute(
        #     select(ApiKey).where(
        #         ApiKey.key_hash == key_hash,
        #         ApiKey.is_active == True,
        #         or_(ApiKey.expires_at.is_(None), ApiKey.expires_at > datetime.utcnow())
        #     )
        # )
        # 
        # api_key_obj = result.scalar_one_or_none()
        
        # 模拟查询结果
        api_key_obj = None  # 这里应该从数据库查询
        
        if api_key_obj:
            # 更新最后使用时间
            api_key_obj.last_used = datetime.utcnow()
            await db.commit()
        
        return api_key_obj


# ====== 权限检查器 ======

class PermissionChecker:
    """权限检查器"""
    
    @staticmethod
    def has_permission(user_permissions: List[str], required_permission: str) -> bool:
        """检查是否有权限"""
        # 超级管理员权限
        if Permission.SYSTEM_ADMIN in user_permissions:
            return True
        
        # 检查具体权限
        return required_permission in user_permissions
    
    @staticmethod
    def has_any_permission(user_permissions: List[str], required_permissions: List[str]) -> bool:
        """检查是否有任意一个权限"""
        # 超级管理员权限
        if Permission.SYSTEM_ADMIN in user_permissions:
            return True
        
        # 检查是否有任意权限
        return any(perm in user_permissions for perm in required_permissions)
    
    @staticmethod
    def has_all_permissions(user_permissions: List[str], required_permissions: List[str]) -> bool:
        """检查是否有所有权限"""
        # 超级管理员权限
        if Permission.SYSTEM_ADMIN in user_permissions:
            return True
        
        # 检查是否有所有权限
        return all(perm in user_permissions for perm in required_permissions)


# ====== 认证依赖 ======

async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Security(security),
    db = Depends(get_db)
) -> User:
    """获取当前用户"""
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="无法验证凭据",
        headers={"WWW-Authenticate": "Bearer"},
    )
    
    try:
        # 验证JWT令牌
        token_data = TokenManager.verify_token(credentials.credentials)
        
        if token_data is None or token_data.user_id is None:
            log_security_event("INVALID_TOKEN", ip_address="unknown")
            raise credentials_exception
        
        # 从数据库获取用户
        # user = await db.get(User, token_data.user_id)
        
        # 模拟用户数据
        user = {
            "id": token_data.user_id,
            "username": token_data.username,
            "permissions": token_data.permissions,
            "is_active": True
        }
        
        if not user or not user.get("is_active"):
            log_security_event("INACTIVE_USER", user_id=token_data.user_id)
            raise credentials_exception
        
        return user
        
    except jwt.JWTError:
        log_security_event("JWT_ERROR")
        raise credentials_exception


async def get_current_user_from_api_key(
    api_key: Optional[str] = Security(api_key_header),
    db = Depends(get_db)
) -> Optional[User]:
    """通过API密钥获取用户"""
    if not api_key:
        return None
    
    try:
        # 验证API密钥
        api_key_obj = await ApiKeyManager.verify_api_key(db, api_key)
        
        if not api_key_obj:
            log_security_event("INVALID_API_KEY", api_key=api_key[:10] + "...")
            return None
        
        # 获取用户
        # user = await db.get(User, api_key_obj.user_id)
        
        # 模拟用户数据
        user = {
            "id": api_key_obj.user_id,
            "username": "api_user",
            "permissions": api_key_obj.permissions,
            "is_active": True
        }
        
        return user
        
    except Exception as e:
        logger.error(f"API密钥验证失败: {str(e)}")
        return None


async def get_user_with_fallback(
    user_jwt: User = Depends(get_current_user),
    user_api: Optional[User] = Depends(get_current_user_from_api_key)
) -> User:
    """获取用户（JWT或API密钥）"""
    if user_jwt:
        return user_jwt
    elif user_api:
        return user_api
    else:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="需要认证"
        )


def require_permission(permission: str):
    """权限检查装饰器"""
    def permission_checker(current_user: User = Depends(get_user_with_fallback)):
        if not PermissionChecker.has_permission(current_user.get("permissions", []), permission):
            log_security_event(
                "PERMISSION_DENIED", 
                user_id=current_user.get("id"),
                required_permission=permission
            )
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"缺少权限: {permission}"
            )
        return current_user
    
    return permission_checker


def require_any_permission(permissions: List[str]):
    """任意权限检查装饰器"""
    def permission_checker(current_user: User = Depends(get_user_with_fallback)):
        if not PermissionChecker.has_any_permission(current_user.get("permissions", []), permissions):
            log_security_event(
                "PERMISSION_DENIED",
                user_id=current_user.get("id"), 
                required_permissions=permissions
            )
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"缺少权限: {' 或 '.join(permissions)}"
            )
        return current_user
    
    return permission_checker


# ====== 认证服务 ======

class AuthService:
    """认证服务"""
    
    @staticmethod
    async def register_user(
        db,
        user_data: RegisterRequest
    ) -> tuple[User, str]:
        """注册用户"""
        # 检查用户名和邮箱是否已存在
        # existing_user = await db.execute(
        #     select(User).where(
        #         or_(User.username == user_data.username, User.email == user_data.email)
        #     )
        # )
        
        # if existing_user.scalar_one_or_none():
        #     raise HTTPException(
        #         status_code=status.HTTP_400_BAD_REQUEST,
        #         detail="用户名或邮箱已存在"
        #     )
        
        # 检查密码强度
        password_check = PasswordManager.check_password_strength(user_data.password)
        if password_check["strength"] == "weak":
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="密码强度不足"
            )
        
        # 创建用户
        hashed_password = PasswordManager.hash_password(user_data.password)
        
        user = User(
            username=user_data.username,
            email=user_data.email,
            hashed_password=hashed_password,
            full_name=user_data.full_name,
            is_active=True
        )
        
        # db.add(user)
        # await db.commit()
        # await db.refresh(user)
        
        # 创建访问令牌
        access_token = TokenManager.create_access_token(
            data={
                "sub": user.id,
                "username": user.username,
                "permissions": Permission.ROLES["user"]
            }
        )
        
        log_security_event("USER_REGISTERED", user_id=user.id)
        
        return user, access_token
    
    @staticmethod
    async def login_user(
        db,
        login_data: LoginRequest
    ) -> Token:
        """用户登录"""
        # 查找用户
        # result = await db.execute(
        #     select(User).where(User.username == login_data.username)
        # )
        # user = result.scalar_one_or_none()
        
        # 模拟用户数据
        user = None
        if login_data.username == "admin" and login_data.password == "admin123":
            user = {
                "id": "admin_user_id",
                "username": "admin", 
                "hashed_password": PasswordManager.hash_password("admin123"),
                "is_active": True,
                "permissions": Permission.ROLES["admin"]
            }
        
        if not user or not user.get("is_active"):
            log_security_event("LOGIN_FAILED", username=login_data.username)
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码错误"
            )
        
        # 验证密码
        if not PasswordManager.verify_password(login_data.password, user.get("hashed_password")):
            log_security_event("LOGIN_FAILED", user_id=user.get("id"), reason="wrong_password")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码错误"
            )
        
        # 创建访问令牌
        access_token_expires = timedelta(
            minutes=ACCESS_TOKEN_EXPIRE_MINUTES * (7 if login_data.remember_me else 1)
        )
        
        access_token = TokenManager.create_access_token(
            data={
                "sub": user.get("id"),
                "username": user.get("username"),
                "permissions": user.get("permissions", [])
            },
            expires_delta=access_token_expires
        )
        
        # 更新最后登录时间
        # user.last_login = datetime.utcnow()
        # await db.commit()
        
        log_security_event("LOGIN_SUCCESS", user_id=user.get("id"))
        
        return Token(
            access_token=access_token,
            token_type="bearer",
            expires_in=int(access_token_expires.total_seconds()),
            user_id=user.get("id"),
            permissions=user.get("permissions", [])
        )
    
    @staticmethod
    async def change_password(
        db,
        user_id: str,
        password_data: PasswordChangeRequest
    ) -> bool:
        """修改密码"""
        # user = await db.get(User, user_id)
        
        # 模拟获取用户
        user = {"hashed_password": PasswordManager.hash_password("old_password")}
        
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="用户不存在"
            )
        
        # 验证当前密码
        if not PasswordManager.verify_password(
            password_data.current_password, 
            user.get("hashed_password")
        ):
            log_security_event("PASSWORD_CHANGE_FAILED", user_id=user_id, reason="wrong_current_password")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="当前密码错误"
            )
        
        # 检查新密码强度
        password_check = PasswordManager.check_password_strength(password_data.new_password)
        if password_check["strength"] == "weak":
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="新密码强度不足"
            )
        
        # 更新密码
        # user.hashed_password = PasswordManager.hash_password(password_data.new_password)
        # await db.commit()
        
        log_security_event("PASSWORD_CHANGED", user_id=user_id)
        
        return True


# 导出
__all__ = [
    # 模型
    "Token", "TokenData", "LoginRequest", "RegisterRequest", 
    "PasswordChangeRequest", "PasswordResetRequest",
    "ApiKeyCreateRequest", "ApiKeyResponse",
    
    # 管理器
    "PasswordManager", "TokenManager", "ApiKeyManager", "PermissionChecker",
    
    # 依赖
    "get_current_user", "get_current_user_from_api_key", "get_user_with_fallback",
    "require_permission", "require_any_permission",
    
    # 服务
    "AuthService",
    
    # 权限
    "Permission",
]